package org.vacoor.xqq.ui.comp.trayicon;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Map;
import java.util.Stack;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 托盘动画图标
 * <p/>
 * User: vacoor
 */
public class TrayAnimatedIcon extends FixX11TransparencyTrayIcon {
    private static final Image EMPTY_IMG = new ImageIcon("").getImage();

    private AnimationPlayer animationPlayer;

    TrayAnimatedIcon(Image image) {
        this(image, null);
    }

    TrayAnimatedIcon(Image image, String tooltip) {
        this(image, tooltip, null);
    }

    TrayAnimatedIcon(Image image, String tooltip, PopupMenu popup) {
        super(image, tooltip, popup);
    }

    /**
     * 如果希望从托盘中移除 TrayAnimatedIcon 对象, 应该先 removeAllAnimation() 或调用该方法
     * 该方法停止所有动画, 并且从托盘移除
     */
    public void removeToSysTray() {
        if (SystemTray.isSupported()) {
            removeAllAnimation();
            SystemTray.getSystemTray().remove(this);
        }
    }

    /**
     * 添加一个跳动
     *
     * @param clear 是否清除之前的跳动
     */
    public void addFlashAnimation(int identifier, Image img, boolean clear) {
        addFlashAnimation(identifier, 300, img, clear);
    }

    public void addFlashAnimation(int identifier, int delay, Image img, boolean clear) {
        if (img == null) {
            return;
        }
        addAnimation(identifier, delay, new Image[]{img, EMPTY_IMG}, clear);
    }

    public void addAnimation(int identifier, int delay, Image[] imgs, boolean clear) {
        ensureAnimationPlayer();
        animationPlayer.addAnimation(identifier, new Animation(delay, imgs), clear);
    }

    /**
     * 删除给定标识的托盘动画
     *
     * @param identifier
     */
    public void removeAnimation(int identifier) {
        if(animationPlayer == null) {
            return;
        }
        animationPlayer.removeAnimation(identifier);
    }

    public void removeAllAnimation() {
        if(animationPlayer == null) {
            return;
        }
        animationPlayer.removeAllAnimation();
    }

    private void ensureAnimationPlayer() {
        if (animationPlayer != null) {
            return;
        }
        synchronized (this) {
            if (animationPlayer == null) {
                animationPlayer = new AnimationPlayer();
            }
        }
    }

    // 托盘跳动动画
    private class AnimationPlayer implements ActionListener {
        private Stack<Integer> animationStack = new Stack<Integer>();
        private Map<Integer, Animation> animations = new ConcurrentHashMap<Integer, Animation>();
        private Image preImg;
        private Timer timer;

        public AnimationPlayer() {
            timer = new Timer(100, this);
        }

        public void addAnimation(int identifier, Animation animation, boolean clear) {
            if (animation == null) {
                return;
            }
            if (clear) {
                animationStack.clear();
                animations.clear();
            }
            animationStack.push(identifier);
            animations.put(identifier, animation);
            if (!timer.isRunning()) {
                preImg = getImage();
                timer.start();
            }
        }

        public void removeAnimation(Integer identifier) {
            animationStack.remove(identifier);
            animations.remove(identifier);
            if (animationStack.size() < 1) {
                timer.stop();
                setImage(preImg);
            }
        }

        public void removeAllAnimation() {
            animationStack.clear();
            animations.clear();
            timer.stop();
            setImage(preImg);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            Animation animation = animations.get(animationStack.peek());
            timer.setDelay(animation.delay);
            setImage(animation.nextFrame());
        }
    }

    private class Animation {
        Image[] imgs;
        int currentFrame;
        int delay;

        Animation(int delay, Image[] imgs) {
            this.delay = delay;
            this.imgs = imgs;
        }

        Image nextFrame() {
            return imgs[currentFrame < imgs.length ? currentFrame++ : (currentFrame = 0)];
        }
    }
}
